'''
tickets1, webapp to insert barcode to SVG templates from database.
Copyright (C) 2018 Rodrigo Garcia

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
'''
import barcode
import subprocess
import os

import qrcode
import qrcode.image.svg

from datetime import datetime

#from .models import Ticket, GrupoTickets, Tienda

STATIC_SVGS = 'static/tickets/'
STATIC_SVG_TEMPLATES_FOLDER = 'static/tickets/templates/'
STATIC_GENERATED_SVGS = 'static/tickets/generated/'

def generarCodioDeBarras(nro, ruta=None):
    ''' Genera el codigo de barras en formato ean13 y lo guarda en 
    un archivo con nombre similar y como svg
    return -- el nombre del archivo generado
    '''
    EAN = barcode.get_barcode_class('ean13')
    ean = EAN(nro)
    # ajustando espaciado entre el texto
    ean.default_writer_options['text_distance']=3.0
    # reducir el espaciado con el margen del svg final
    ean.default_writer_options['quiet_zone'] = 0.1
    if ruta is None:
        return ean.save(nro)
    return ean.save(filename=os.path.join(ruta, nro))

def generarCodigoQr(texto, ruta, filename=None):
    ''' Genera el codigo qr y lo guarda en un archivo como svg.
    texto -- Texto que se guarda en el SVG
    ruta -- el directorio donde se guardara el archivo (obligatorio)
    filename -- Nombre del archivo en donde se guarda (si no se proporciona
    usa un nombre numerico al azar)

    return -- nombre del archivo generado.svg
    '''
    #factory = qrcode.image.svg.SvgPathImage
    factory = qrcode.image.svg.SvgImage
    img = qrcode.make(texto, image_factory=factory)
    
    
    if filename is None:
        import random
        rand = random.randrange(1,1000)
        img.save(os.path.join(ruta, str(rand)+'.svg'))
        return os.path.join(ruta, str(rand)+'.svg')
    img.save(os.path.join(ruta, filename))
    return os.path.join(ruta, filename)

def contenidoArchivo(filename=None):
    ''' Retorna una cadena con el contenido del archivo dado'''
    if filename is None:
        return None
    c = ''
    try:
        with open(filename, 'r') as fil:
            c = fil.read()
    except IOError as e:
        print (e)
        return None
    return c

def escribirArchivo(filename=None, contenido=None):
    ''' Escribe el `contenido`  en el archivo `filename`
    devuelve False si hay errores'''
    if filename is None:
        return False
    try:
        with open(filename, 'w') as fil:
            fil.write(contenido)
    except IOError as e:
        print (e)
        return False
    return True

def eliminarArchivo(filename=None):
    ''' Elimina el archivo `filename' 
    filename -- nombre del archivo con ruta (obligatorio)
    return -- True si el archivo se ha eliminado'''
    if filename is None:
        return False
    try: 
        cmd = ['rm', '-f', filename]
        subprocess.check_call(cmd)
        return True
    except subprocess.SubprocessError as e:
        print (e)
        return False

def introducirBarcodeSVGEnOtroSVG(filename_barcode, filename_svg):
    ''' Introduce parte del SVG de `filename_barcode' en `filename_svg`
    el contenido es introducido en un pocisiones fija y aplicando
    una transformacion en la etiqueta '<g' fija

    return -- el nombre del archivo si consigue escribir o None en otro caso
    '''
    barcode_cont = contenidoArchivo(filename_barcode)
    osvg_cont = contenidoArchivo(filename_svg)

    if barcode_cont is None or osvg_cont is None:
        return None
    barcode_cont = barcode_cont.split('<svg')[1]
    osvg_cont = osvg_cont[0:-6] # quitando el ultimo </svg>

    # introduciendo en grupo y agregando transofrmacion
    # en la siguiente linea se introducen 2 veces el svg pero con parametros
    # diferentes ver (transform="matrix...") que quedan en lugares distintos
    #barcode_cont = '<g id="barcode_volcado"\n\ttransform="matrix(0,1.2379845,-0.4466589,0,271.09792,1.579233)">\n<svg' + barcode_cont + '</g>\n' + '<g id="barcode_pequenyo"\n\ttransform="matrix(0.63169051,0,0,0.33599068,326.07055,116.87741)">\n<svg' + barcode_cont + '</g>'

    matrix_conv = 'matrix(1.2304505,0,0,0.6969245,2.121157,270.37724)'
    barcode_cont = '<g id="barcode_volcado"\n\ttransform="' + matrix_conv + '">\n<svg' + barcode_cont + '</g>'
    
    # terminando y uniendo
    osvg_cont += '\n' + barcode_cont + '\n</svg>'
    
    # escribiendo cambios en archivo
    if escribirArchivo(filename_barcode, osvg_cont):
        return filename_barcode
    return None

def introducirQrCodeSvgEnOtroSvg(filename_qrcode, filename_svg):
    ''' Introduce parte del SVG `filename_qrcode' en `filename_svg`
    el contenido es introducido en pocisiones fijas y aplicando una
    transformacion en la etiqueta '<g' fija

    filename_qrcode -- nombre del archivo con el codigo QR
    filename_svf -- nombre del archivo svg donde se introduce `filename_qrcode'
    return -- el nombre del archivo si consigue escribir
    '''
    qrcode_cont = contenidoArchivo(filename_qrcode)
    osvg_cont = contenidoArchivo(filename_svg)

    if qrcode_cont is None or osvg_cont is None:
        return None
    qrcode_cont = qrcode_cont.split('<svg')[1]
    osvg_cont = osvg_cont[0:-6] # quitando el uiltimo </svg>

    # introduciendo en grupo y agregando transformacion
    matrix_conv = 'matrix(0.47776092,0,0,0.47776092,27.256147,333.10615)'
    qrcode_cont = '<g id="qrcode"\n\ttransform="' + matrix_conv + '">\n<svg' + qrcode_cont + '</g>'

    # terminando y uniendo
    osvg_cont += '\n' + qrcode_cont + '\n</svg>'

    # escribiendo cambios en archivo
    if escribirArchivo(filename_qrcode, osvg_cont):
        return filename_qrcode
    return None
    
def reemplazarEtiquetasEnSVG(dict_etiquetas, filename_svg):
    '''Introduce las etiquetas contenidas en el diccionario `dict_etiquetas' al
    archivo SVG `filename_svg' reemplazando {{ etiqueta }} del SVG por el valor que se
    encuentre en el diccionario pasado. 
    Guarda el contenido reemplazado en `filename_svg'

    return: True si escribe correctamente, False en otro caso
    '''
    content = contenidoArchivo(filename_svg)
    if content is None:
        return False
    # reemplazando valores
    for key, value in dict_etiquetas.items():
        content = content.replace("{{"+key+"}}", str(value))
    return escribirArchivo(filename_svg, content)

def dicEtiquetasParaSVG(ticket):
    ''' Retorna un diccionario con las etiquetas que se van a reemplazar
    en los templates SVG '''
    if ticket is None:
        return None
    
    dict = {
        'NoFactura': ticket.nro_factura,
        'NoNIT': ticket.grupo_tickets.tienda.nit, # referencia la tienda a la que pertenece
        'CodControl': ticket.cod_control,
        #'emision': String(ticket.grupo_tickets.fecha_creacion),
        'Costo': str(ticket.grupo_tickets.precio.costo_numeral),
        'CostoLiteral': ticket.grupo_tickets.precio.costo_literal
        }
    return dict

def generarPdfDesdeSVG(filename_svg):
    ''' Exporta el archivo svg a pdf usando inkscape
    filename_svg -- Nombre del archivo svg con ruta absoluta (obligatorio)
    return -- Nombre del archivo pdf generado
    '''
    if filename_svg is None:
        return None
    try:
        cmd = ['inkscape', '--without-gui', '-d 1200', \
               '--export-pdf={}.pdf'.format(filename_svg), filename_svg]
        subprocess.check_call(cmd)
        return filename_svg+'.pdf'
    except subprocess.SubprocessError as e:
        print (e)
    return None

def exportarSVGsEnPdfs(tickets):
    ''' Exporta en PDF la lista de objetos tickets que se reemplazan en su
    plantilla SVG, fuerza la generacion del SVG.
    
    tikcets -- el arreglo de tickets a recibir (mandatory)
    return -- los nombres de pdfs exportados
    '''
    if tickets is None:
        return 0
    s = []
    for ticket in tickets:
        if ticket.svg_generado != '-':
            # generando el archivo pdf usando inkscape
            name = ticket.svg_generado
            result = "{}.pdf".format(name)
            try: 
                cmd = ['inkscape', '--without-gui', '-d 1200', \
                      '--export-pdf={}'.format(result), name]
                subprocess.check_call(cmd)
                s.append(result)
            except subprocess.SubprocessError as e:
                print (e)
    return s

def unirGrupoDePdfs(archivos, nombre=None):
    ''' Recibe una lista de archivos pdf y los une en un solo pdf de salida.

    archivos -- lista con los nombres de archivos pdf (obligatorio)
    nombre -- nombre de archivo que se requiere generar, sin extension pdf (default: None)
    return -- nombre del archivo pdf generado.
    '''
    if archivos is None:
        return None
    # usando pdftk
    try:
        subprocess.check_call('pwd')
        if nombre is None:
            dt = datetime.now()
            salida = STATIC_GENERATED_SVGS + str(dt.date())+'_'+str(dt.hour)+'-'+str(dt.minute)
        else:
            salida = os.path.join(STATIC_GENERATED_SVGS, nombre)
        # nota sin la opcion -v o mejor dicho sin un argumento extra
        # no se generan toman en cuenta todos los pdfs
        args = ['-v']+ archivos + ['output', '{}.pdf'.format(salida)]
        #print ('---> args:'+str(args))
        s = subprocess.Popen(executable='pdftk', args=args)
        s.wait() # espera a que el subproceso termine antes de continuar
    except subprocess.SubprocessError as e:
        print (e)
        return ''
    return salida+'.pdf'

def UnicoPdf(archivos, eliminar_svgs=False, nombre=None):
    ''' Recibe una lista de archivos pdf, usa `unirGrupoDePdfs()' para
    obtener un solo pdf y elimina los archivos pdf utilizados.
    
    archivos -- lista con los nombres de archivos pdf (obligatorio)
    eliminar_svgs -- Para eliminar los svgs tambien (default: False)
    nombre -- Nombre del archivo (sin ruta) que se requiere en la salida  (dafault: None)
    return -- nombre del archivo pdf generado
    '''
    if archivos is None:
        return None
    pdf = unirGrupoDePdfs(archivos, nombre=nombre)
    if pdf == '':
        return None
    #eliminando
    try:
        cmd = ['rm', '-f'] + archivos
        subprocess.check_call(cmd)
        if eliminar_svgs:
            cmd = ['rm' ,'-f'] + [ a.split('.pdf')[0] for a in archivos]
            subprocess.check_call(cmd)
    except subprocess.SubprocessError as e:
        print (e)
        return ''
    return pdf

def empaquetarPdfsGenerados(nombres):
    ''' Utiliza tar para empaquetar varios archivos dados y devuelve
    el nombre del archivo empaquetado o None si hay errores

    nombres -- el nombre del archivo .tar.gz empaquetado
    '''
    if nombres is None:
        return None

    dt = datetime.now()
    tarball = STATIC_GENERATED_SVGS + str(dt.date())+'_'+str(dt.hour)+'-'+str(dt.minute)
    # command
    try:
        subprocess.check_call('pwd')
        args = ['-zcvf', '{}.tar.gz'.format(tarball)] + nombres
        subprocess.Popen(executable='tar', args=args, shell=True)

    except subprocess.SubprocessError as e:
        print(e)
    return tarball+'.tar.gz'
